package com.shoulder.mybatis.interceptor;

import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.session.Configuration;

import java.sql.Connection;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * MyBatis SQL 日志拦截器（精简版）
 * 功能：打印带参数值的 SQL 语句，自动替换 ? 为实际值
 */
@Slf4j
@Intercepts({
        @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
public class SqlLoggerInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        long start = System.nanoTime();
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();

        try {
            Object result = invocation.proceed();
            long timeCost = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);

            MetaObject metaObject = MetaObject.forObject(
                    statementHandler,
                    SystemMetaObject.DEFAULT_OBJECT_FACTORY,
                    SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY,
                    new DefaultReflectorFactory());

            MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
            BoundSql boundSql = statementHandler.getBoundSql();
            String sqlId = mappedStatement.getId();

            // 始终格式化 SQL，替换参数
            String formattedSql = getFormattedSql(mappedStatement.getConfiguration(), boundSql, sqlId);

            // 打印 SQL 和耗时
            log.info("SQL ({}ms) : {}", timeCost, formattedSql);

            return result;

        } catch (Exception e) {
            log.error("SQL logging failed for {}", invocation.getTarget().getClass().getSimpleName(), e);
            throw e;
        }
    }

    /**
     * 获取带参数值的完整 SQL（替换 ? 为实际值）
     */
    private String getFormattedSql(Configuration configuration, BoundSql boundSql, String sqlId) {
        try {
            String sql = boundSql.getSql().replaceAll("\\s+", " ").trim();
            Object parameterObject = boundSql.getParameterObject();
            List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();

            // 如果没有参数，直接返回带 sqlId 的 SQL
            if (parameterMappings == null || parameterMappings.isEmpty() || parameterObject == null) {
                return sqlId + " ==> " + sql;
            }

            StringBuilder sqlBuilder = new StringBuilder(sqlId).append(" ==> ");
            int parameterIndex = 0;
            int lastPos = 0;

            char[] chars = sql.toCharArray();
            for (int i = 0; i < chars.length; i++) {
                if (chars[i] == '?') {
                    sqlBuilder.append(chars, lastPos, i - lastPos);

                    if (parameterIndex >= parameterMappings.size()) {
                        sqlBuilder.append("[TOO_MANY_?]");
                    } else {
                        ParameterMapping mapping = parameterMappings.get(parameterIndex++);
                        Object value = getParameterValue(configuration, parameterObject, boundSql, mapping);
                        sqlBuilder.append(formatParameterValue(value));
                    }
                    lastPos = i + 1;
                }
            }
            // 添加最后部分
            sqlBuilder.append(chars, lastPos, chars.length - lastPos);

            return sqlBuilder.toString().trim();
        } catch (Exception e) {
            log.error("Failed to format SQL with parameters", e);
            return sqlId + " ==> [FORMAT_ERROR] " + boundSql.getSql();
        }
    }

    /**
     * 安全获取参数值（支持 additionalParameter 和 POJO 属性）
     */
    private Object getParameterValue(Configuration config, Object paramObj, BoundSql boundSql, ParameterMapping mapping) {
        try {
            String propertyName = mapping.getProperty();
            if (boundSql.hasAdditionalParameter(propertyName)) {
                return boundSql.getAdditionalParameter(propertyName);
            } else {
                MetaObject metaObj = config.newMetaObject(paramObj);
                return metaObj.hasGetter(propertyName) ? metaObj.getValue(propertyName) : null;
            }
        } catch (Exception e) {
            log.warn("Failed to get parameter value for property: {}", mapping.getProperty(), e);
            return "[GET_ERROR]";
        }
    }

    /**
     * 将参数值格式化为 SQL 字符串
     */
    private String formatParameterValue(Object value) {
        if (value == null) {
            return "NULL";
        } else if (value instanceof String) {
            return "'" + escapeSingleQuote(value.toString()) + "'";
        } else if (value instanceof Date) {
            return "'" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format((Date) value) + "'";
        } else if (value instanceof Number || value instanceof Boolean) {
            return value.toString();
        } else if (value.getClass().isArray()) {
            return Arrays.stream((Object[]) value)
                    .map(this::formatParameterValue)
                    .collect(Collectors.joining(", ", "(", ")"));
        } else if (value instanceof Collection) {
            return ((Collection<?>) value).stream()
                    .map(this::formatParameterValue)
                    .collect(Collectors.joining(", ", "(", ")"));
        } else {
            try {
                String str = value.toString();
                return "'" + (str.contains("'") ? str.replace("'", "''") : str) + "'";
            } catch (Throwable t) {
                return "'[toString_failed:" + value.getClass().getSimpleName() + "@" + Integer.toHexString(System.identityHashCode(value)) + "]'";
            }
        }
    }

    /**
     * 转义单引号，防止 SQL 字符串错误
     */
    private String escapeSingleQuote(String str) {
        return str.replace("'", "''");
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
        // 无需配置，空实现
    }
}